------------------------------------------
-- 绝望深渊

-- 角色db
-- db.lianyu.maxLv 			最大通关数
-- db.lianyu.firstreward 	首通奖励领取标记 [id]=true
-- db.lianyu.todayMaxLv 	[每日重置]今日0点最大通关
-- db.lianyu.todayWinLv		[每日重置]今日成功挑战x关
-- db.lianyu.todayHelpUuid 	[每日重置]助战目标

-- 角色d缓存
-- 角色
-- human.db.lianyuCache
-- lianyuCache.bufferList 		可选buffer列表
-- lianyuCache.bufferIndex 		已选择buffer
-- lianyuCache.combatHelp 		支援对象战斗数据
-- lianyuCache.combatHelpPos 	支援对象站位
-- lianyuCache.startLv 			起始关
-- lianyuCache.winLv 			胜利关数
-- lianyuCache.oldWinLv			老的胜利关数
-- lianyuCache.combatHpRates 	剩余血量 [pos]=rate
-- lianyuCache.combatResult 	最近战斗结果

-- common db
-- [uuid] = {heroGrid,combatObj,rolebase,helpCnt,helpUuids}
------------------------------------------

local LianyuExcel = require("excel.lianyu")
local MailExcel = require("excel.mail")
local Util = require("common.Util")
local Lang = require("common.Lang")
local CommonDB = require("common.CommonDB")
local Msg = require("core.Msg")
local ObjHuman = require("core.ObjHuman")
local Broadcast = require("broadcast.Broadcast")
local Grid = require("bag.Grid")
local BagLogic = require("bag.BagLogic")
local ItemDefine = require("bag.ItemDefine")
local BRoleLogic = require("billboard.BRoleLogic")
local BillboardDB = require("billboard.BillboardDB")
local BillboardDefine = require("billboard.BillboardDefine")
local CombatObj = require("combat.CombatObj")
local CombatImpl = require("combat.CombatImpl")
local CombatLogic = require("combat.CombatLogic")
local CombatDefine = require("combat.CombatDefine")
local CombatPosLogic = require("combat.CombatPosLogic")
local RoleAttr = require("role.RoleAttr")
local RoleLogic = require("role.RoleLogic")
local RoleDefine = require("role.RoleDefine")
local RoleDBLogic = require("role.RoleDBLogic")
local RoleSystemLogic = require("roleSystem.RoleSystemLogic")
local RoleSystemDefine = require("roleSystem.RoleSystemDefine")
local FriendDBLogic = require("friend.FriendDBLogic")
local HeroGrid = require("hero.HeroGrid")
local HeroLogic = require("hero.HeroLogic")
local MailManager = require("mail.MailManager")
local MailDefine = require("mail.MailIdDefine")
local DailyTaskLogic = require("dailyTask.DailyTaskLogic")
local Timer = require("core.Timer")
local JibanLogic = require("combat.JibanLogic")
local MengxinLogic = require("present.MengxinLogic")
local Log = require("common.Log")
local YunYingLogic = require("yunying.YunYingLogic")
local BeSkill = require("combat.BeSkill")
local HeroDefine = require("hero.HeroDefine")

local PER_FIVELEVEL = 5 --每隔5关(首通奖励,buffer选择)
local STARTLEVEL_DIFF = 10 --起始关卡差值
local FRIEND_DEGREE_MAX = 10 --活跃度次数上限
local REWARD_SHOW_MAXCNT = 10 --奖励展示数量
local MAX_HEROCOMBAT_MUL = 120 --最大倍数百分比
local MAX_HP_RATE = 10000 --血量万分比
local FRIEND_HELP_MAXCNT = 10 --获得助战奖励次数
local FRIEND_ITEM_CNT = 10 --每次助战获得x点友情点

local STATUS_START_BUTTON0 = 0 -- 开始挑战(红点)
local STATUS_START_BUTTON1 = 1 -- 重新开始
local STATUS_START_BUTTON2 = 2 -- 重新开始(5关奖励全领了)

-- ============================== db ==================================
-- 最大通关数
function getMaxLv(human)
    if not human.db.lianyu then
        return 0
    end
    return human.db.lianyu.maxLv or 0
end

function setMaxLv(human, maxLv)
    if not human.db.lianyu then
        human.db.lianyu = {}
    end
    human.db.lianyu.maxLv = maxLv
    BRoleLogic.updateData(BillboardDefine.TYPE_LIANYU, human.db)
end

-- 首通奖励
function isFirstGet(human, id)
    if not human.db.lianyu then
        return
    end
    if not human.db.lianyu.firstreward then
        return
    end
    return human.db.lianyu.firstreward[id]
end

function setFirstGet(human, id)
    if not human.db.lianyu then
        human.db.lianyu = {}
    end
    if not human.db.lianyu.firstreward then
        human.db.lianyu.firstreward = {}
    end
    human.db.lianyu.firstreward[id] = true
end

-- 获取本日起始关
function getTodayStartLv(human)
    local lianyuDB = human.db.lianyu
    if not lianyuDB or not lianyuDB.todayMaxLv then
        return 1
    end
    return math.max(lianyuDB.todayMaxLv - STARTLEVEL_DIFF, 1)
end

-- 本日已胜利关卡
function getTodayWinLv(human)
    local lianyuDB = human.db.lianyu
    if not lianyuDB then
        return
    end
    return lianyuDB.todayWinLv
end

function setTodayWinLv(human, todayWinLv)
    if not human.db.lianyu then
        human.db.lianyu = {}
    end
    human.db.lianyu.todayWinLv = todayWinLv
end

-- 获取助战信息
function getTodayHelp(human)
    local lianyuDB = human.db.lianyu
    if not lianyuDB then
        return
    end
    local todayHelpUuid = lianyuDB.todayHelpUuid
    if not todayHelpUuid then
        return
    end
    if not FriendDBLogic.isFriend(human.db._id, todayHelpUuid) then
        lianyuDB.todayHelpUuid = nil
        return
    end
    local lyCommon = getLianyuCommon()
    local helpInfo = getHelpInfo(lyCommon, todayHelpUuid)
    return helpInfo, todayHelpUuid
end

-- 设置新的助战
function setTodayHelp(human, todayHelpUuid)
    if not human.db.lianyu then
        human.db.lianyu = {}
    end
    human.db.lianyu.todayHelpUuid = todayHelpUuid
end

-- ============================== 缓存 ==================================

function cleanCache(human)
    human.db.lianyuCache = nil
end

function getLianyuCache(human)
    return human.db.lianyuCache
end

-- 初始
function initCombatCache(human)
    human.db.lianyuCache = {}
    local lianyuCache = human.db.lianyuCache
    lianyuCache.startLv = getTodayStartLv(human)
    lianyuCache.oldWinLv = getTodayWinLv(human) or 0
    lianyuCache.winLv = nil
    lianyuCache.bufferList = nil
    lianyuCache.bufferIndex = nil
    lianyuCache.combatHelp = nil
    lianyuCache.combatHelpPos = nil
    lianyuCache.combatResult = nil
    lianyuCache.combatHpRates = nil
    lianyuCache.combatObjSkill = nil
    lianyuCache.combatHelpSkill = nil
    lianyuCache.objList = nil
    lianyuCache.helpList = nil
    lianyuCache.rolebase = nil
    lianyuCache.formation = nil
    lianyuCache.jiban = nil
    lianyuCache.backupPos2index = nil -- 援军信息 [战斗位置] = 援军的index
    randBuffers(human)
    return lianyuCache
end

-- 根据类型随机buffer
local BUFFER_KEY_LIST = {"buff1", "buff2", "buff3"}
function randBufferByType(bufferType)
    local keyName = BUFFER_KEY_LIST[bufferType]
    if not keyName then
        return
    end

    local list = LianyuExcel.bufferitem[1][keyName]
    if not list then
        return
    end

    local weightSum = 0
    for _, cf in ipairs(list) do
        local weight = cf[1]
        weightSum = weightSum + weight
    end
    if weightSum < 1 then
        return
    end
    local r = math.random(1, weightSum)
    for _, cf in ipairs(list) do
        local weight = cf[1]
        local itemID = cf[2]
        if r <= weight then
            return itemID
        end
        r = r - weight
    end
end

-- 随机buffer列表
function randBuffers(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    lianyuCache.bufferList = {}
    lianyuCache.bufferIndex = nil
    for bufferType = 1, #BUFFER_KEY_LIST do
        local len = #lianyuCache.bufferList
        local itemID = randBufferByType(bufferType)
        lianyuCache.bufferList[len + 1] = itemID
    end
end

-- 设置已选择的buffer
function setBufferIndex(human, bufferIndex)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    if not lianyuCache.bufferList then
        return
    end
    local itemID = lianyuCache.bufferList[bufferIndex]
    if not itemID then
        return
    end
    lianyuCache.bufferIndex = bufferIndex
end

-- 获取已选择的buffer
function getSelectBuffer(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    local bufferIndex = lianyuCache.bufferIndex
    if not bufferIndex then
        return
    end
    return lianyuCache.bufferList[bufferIndex]
end

-- 获取助战对象
function getCombatHelp(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    return lianyuCache.combatHelp, lianyuCache.combatHelpPos
end

-- 助战上阵
function setCombatHelpPos(human, pos)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    if lianyuCache.combatHelpPos then
        return
    end

    local helpInfo = getTodayHelp(human)
    if not helpInfo then
        return
    end

    lianyuCache.combatHelp = helpInfo
    lianyuCache.combatHelpPos = pos
    return lianyuCache.combatHelp
end

-- 获取战斗过程血量
function getCombatHpRate(human, index)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return MAX_HP_RATE
    end
    if not lianyuCache.combatHpRates then
        return MAX_HP_RATE
    end
    return lianyuCache.combatHpRates[index] or MAX_HP_RATE
end

-- ============================== common db ==================================

function getLianyuCommon()
    local lyCommon = CommonDB.getValueByKey(CommonDB.KEY_LIANYU)
    local nowTime = os.time()
    local dayStartTime = Util.getDayStartTime(nowTime)
    if not lyCommon or lyCommon.dayStartTime ~= dayStartTime then
        lyCommon = {}
        lyCommon.dayStartTime = dayStartTime
        lyCommon.list = {}
        CommonDB.updateValue(CommonDB.KEY_LIANYU, lyCommon)
    end
    return lyCommon
end

-- 根据uuid获取助战信息
function getHelpInfo(lyCommon, uuid)
    if not uuid then
        return
    end
    return lyCommon.list[uuid]
end

-- 设置助战信息
function addHelpInfo(lyCommon, uuid, heroGrid, combatObj, rolebase)
    local helpInfo = {}
    helpInfo.uuid = uuid
    helpInfo.heroGrid = heroGrid
    helpInfo.combatObj = combatObj
    helpInfo.rolebase = rolebase
    helpInfo.helpCnt = 0
    helpInfo.helpUuids = {}
    lyCommon.list[uuid] = helpInfo
    CommonDB.updateValue(CommonDB.KEY_LIANYU, lyCommon)
end

-- 是否助战过
function isHelpTarget(helpInfo, targetUuid)
    if helpInfo.helpUuids[targetUuid] then
        return true
    end
end

-- 记录助战
function setHelpTarget(helpInfo, targetUuid)
    helpInfo.helpUuids[targetUuid] = os.time()
    helpInfo.helpCnt = helpInfo.helpCnt + 1
    local lyCommon = getLianyuCommon()
    CommonDB.updateValue(CommonDB.KEY_LIANYU, lyCommon)
end

-- ============================== config ==================================

-- 根据名次获取奖励配置
function getRankConfig(rank)
    for _, config in ipairs(LianyuExcel.rankreward) do
        if rank < config.rankMin then
            break
        end
        if rank <= config.rankMax then
            return config
        end
    end
end

-- 获取前10关未领取的首通奖励id列表
local FIRST_REWARD_IDS = {}
function getFirstRewardIDs(human)
    Util.cleanTable(FIRST_REWARD_IDS)
    for id, config in ipairs(LianyuExcel.gamelevel) do
        if #config.firstrewards > 0 then
            if #FIRST_REWARD_IDS >= REWARD_SHOW_MAXCNT then
                break
            end
            if not isFirstGet(human, id) then
                FIRST_REWARD_IDS[#FIRST_REWARD_IDS + 1] = id
            end
        end
    end
    return FIRST_REWARD_IDS
end

-- 获取最近需要展示的奖励
function getNearFirstReward(human)
    for id, config in ipairs(LianyuExcel.gamelevel) do
        if #config.firstrewards > 0 then
            if not isFirstGet(human, id) then
                return id, config
            end
        end
    end
end

-- ============================== msg ==================================

-- 是否有奖励红点
function hasRewardRed(human)
    local firstNeedLv, firstConfig = getNearFirstReward(human)
    if not firstNeedLv then
        return
    end
    return getMaxLv(human) >= firstNeedLv
end

-- 是否有助战红点
function hasHelpRed(human)
    local lyCommon = getLianyuCommon()
    return not getHelpInfo(lyCommon, human.db._id)
end

-- 主界面查询
function sendQuery(human)
    if not RoleSystemLogic.isOpen(human, RoleSystemDefine.ROLE_SYS_ID_1205, true) then
        return
    end

    ObjHuman.updateDaily(human)
    local msgRet = Msg.gc.GC_LIANYU_QUERY
    local dataNet = msgRet.data
    local maxLv = getMaxLv(human)
    dataNet.maxLv = maxLv

    -- 排名相关
    local board = BillboardDB.getBoard(BillboardDefine.TYPE_LIANYU)
    dataNet.boardList[0] = math.min(#dataNet.boardList, #board.rank2data)
    for i = 1, dataNet.boardList[0] do
        local rankData = board.rank2data[i]
        local boardNet = dataNet.boardList[i]
        local db = RoleDBLogic.getDb(rankData.uuid, "name")
        boardNet.rank = i
        boardNet.name = db and db.name or ""
        boardNet.lv = rankData.value1
    end
    local myRank = BillboardDB.getRank(BillboardDefine.TYPE_LIANYU, human.db._id) or 0
    local myRankConfig = getRankConfig(myRank)
    dataNet.myRank = myRank
    dataNet.myRankItems[0] = myRankConfig and #myRankConfig.rewards or 0
    for i = 1, dataNet.myRankItems[0] do
        local itemID = myRankConfig.rewards[i][1]
        local itemCnt = myRankConfig.rewards[i][2]
        Grid.makeItem(dataNet.myRankItems[i], itemID, itemCnt)
    end

    -- 首通奖励
    local firstNeedLv, firstConfig = getNearFirstReward(human)
    dataNet.firstNeedLv = firstNeedLv or 0
    dataNet.firstItems[0] = firstConfig and #firstConfig.firstrewards or 0
    for i = 1, dataNet.firstItems[0] do
        local itemID = firstConfig.firstrewards[i][1]
        local itemCnt = firstConfig.firstrewards[i][2]
        Grid.makeItem(dataNet.firstItems[i], itemID, itemCnt)
    end
    dataNet.firstLeftCnt = firstNeedLv and math.max(firstNeedLv - maxLv, 0) or 0
    dataNet.rewardRed = hasRewardRed(human) and 1 or 0

    -- 助战相关
    dataNet.helpRed = hasHelpRed(human) and 1 or 0
    dataNet.helpByUuid = ""
    local targetHelp = getTodayHelp(human)
    dataNet.helpHero[0] = 0
    if targetHelp and targetHelp.heroGrid then
        dataNet.helpByUuid = targetHelp.uuid
        dataNet.helpHero[0] = 1
        HeroGrid.makeHeroSimple(dataNet.helpHero[1], targetHelp.heroGrid, targetHelp.heroGrid.bagIndex)
    end

    -- 每日5关奖励
    local todayWinLv = getTodayWinLv(human)
    dataNet.status = STATUS_START_BUTTON0
    if todayWinLv then
        dataNet.status = STATUS_START_BUTTON1
        if todayWinLv >= PER_FIVELEVEL then
            dataNet.status = STATUS_START_BUTTON2
        end
    end
    dataNet.todayStartLv = getTodayStartLv(human)
    dataNet.todayWinLv = todayWinLv or 0
    --	Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 首通奖励查询
function sendFirstRewardQuery(human)
    local list = getFirstRewardIDs(human)
    local maxLv = getMaxLv(human)
    local msgRet = Msg.gc.GC_LIANYU_FIRST_REWARD_QUERY
    msgRet.list[0] = list and #list or 0
    for i = 1, msgRet.list[0] do
        local needLv = list[i]
        local net = msgRet.list[i]
        local config = LianyuExcel.gamelevel[needLv]
        net.needLv = needLv
        net.leftLv = math.max(needLv - maxLv, 0)
        net.items[0] = #config.firstrewards
        for j = 1, net.items[0] do
            local itemID = config.firstrewards[j][1]
            local itemCnt = config.firstrewards[j][2]
            Grid.makeItem(net.items[j], itemID, itemCnt)
        end
    end
    msgRet.rewardRed = hasRewardRed(human) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 领取首通奖励
function getFirstReward(human, needLv, getway)
    local config = LianyuExcel.gamelevel[needLv]
    if not config then
        return
    end
    local maxLv = getMaxLv(human)
    if maxLv < needLv then
        return Broadcast.sendErr(human, Lang.YUNYING_GET_ERR_CONDITION)
    end
    if isFirstGet(human, needLv) then
        return Broadcast.sendErr(human, Lang.YUNYING_GET_ERR_HADGET)
    end

    setFirstGet(human, needLv)
    BagLogic.addItemList(human, config.firstrewards, "lianyu_firstReward")
    -- 0-在主界面领取 1-在首通列表领取 2-在战斗内领取
    if getway == 0 then
        sendQuery(human)
    elseif getway == 1 then
        sendQuery(human)
        sendFirstRewardQuery(human)
    elseif getLianyuCache(human) then
        -- 战斗过程中领奖励
        sendFightInfo(human, true)
    end

    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1205)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1201)
end

-- 每日奖励展示
function sendDailyRewardQuery(human)
    local todayStartLv = getTodayStartLv(human)
    local msgRet = Msg.gc.GC_LIANYU_DAILY_REWARD_QUERY
    msgRet.list[0] = 0
    for i = 1, REWARD_SHOW_MAXCNT do
        local lv = todayStartLv + i - 1
        local config = LianyuExcel.gamelevel[lv]
        if not config then
            break
        end

        msgRet.list[0] = msgRet.list[0] + 1
        local net = msgRet.list[msgRet.list[0]]
        net.needLv = lv
        net.items[0] = #config.totalrewards
        for j = 1, net.items[0] do
            local itemID = config.totalrewards[j][1]
            local itemCnt = config.totalrewards[j][2]
            Grid.makeItem(net.items[j], itemID, itemCnt)
        end
    end
    msgRet.rewardRed = hasRewardRed(human) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 排行榜奖励预览
function sendBoardRewardQuery(human)
    local msgRet = Msg.gc.GC_LIANYU_BOARD_REWARD_QUERY
    msgRet.list[0] = #LianyuExcel.rankreward
    for i = 1, msgRet.list[0] do
        local net = msgRet.list[i]
        local config = LianyuExcel.rankreward[i]
        net.minRank = config.rankMin
        net.maxRank = config.rankMax
        net.items[0] = #config.rewards
        for j = 1, net.items[0] do
            local itemID = config.rewards[j][1]
            local itemCnt = config.rewards[j][2]
            Grid.makeItem(net.items[j], itemID, itemCnt)
        end
    end
    msgRet.rewardRed = hasRewardRed(human) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 查询好友助阵-支援我的
function sendHelpFriendQuery(human)
    local lyCommon = getLianyuCommon()
    local cnt, list = FriendDBLogic.getFriendUuids(human.db._id)
    local maxZhandouli = HeroLogic.getHeroMaxZDL(human) * MAX_HEROCOMBAT_MUL / 100
    local msgRet = Msg.gc.GC_LIANYU_HELP_FRIEND_QUERY
    msgRet.list[0] = 0
    for i = 1, cnt do
        if msgRet.list[0] >= #msgRet.list then
            break
        end

        local targetUuid = list[i].uuid
        local helpInfo = getHelpInfo(lyCommon, targetUuid)
        if helpInfo and helpInfo.heroGrid.zhandouli <= maxZhandouli then
            msgRet.list[0] = msgRet.list[0] + 1
            local net = msgRet.list[msgRet.list[0]]
            net.uuid = targetUuid
            net.name = helpInfo.rolebase.name
            HeroGrid.makeHeroSimple(net.heroSimple, helpInfo.heroGrid)
        end
    end
    local _, selectUuid = getTodayHelp(human)
    msgRet.selectUuid = selectUuid or ""
    msgRet.helpRed = hasHelpRed(human) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 选择助战
function selectHelpFriend(human, selectUuid)
    if not RoleSystemLogic.isOpen(human, RoleSystemDefine.ROLE_SYS_ID_1205, true) then
        return
    end

    if not selectUuid or selectUuid == "" then -- 取消
        setTodayHelp(human, nil)
    else
        if not FriendDBLogic.isFriend(human.db._id, selectUuid) then
            return Broadcast.sendErr(human, Lang.LIANYU_HELP_SELECT_ERR_FRIEND)
        end

        local lyCommon = getLianyuCommon()
        local helpInfo = getHelpInfo(lyCommon, selectUuid)
        if not helpInfo then
            return Broadcast.sendErr(human, Lang.LIANYU_HELP_SELECT_ERR_SET)
        end

        -- 超出战力不能选择
        local maxZhandouli = HeroLogic.getHeroMaxZDL(human)
        if helpInfo.heroGrid.zhandouli > maxZhandouli * MAX_HEROCOMBAT_MUL / 100 then
            return Broadcast.sendErr(human, Util.format(Lang.LIANYU_MY_SELECT_ERR_ZDL, MAX_HEROCOMBAT_MUL))
        end

        setTodayHelp(human, selectUuid)
    end
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1205)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1201)
    sendHelpFriendQuery(human)
    sendQuery(human)
end

-- 查询好友助阵-我的支援
function sendHelpMyQuery(human)
    local lyCommon = getLianyuCommon()
    local helpInfo = getHelpInfo(lyCommon, human.db._id)
    local msgRet = Msg.gc.GC_LIANYU_HELP_MY_QUERY
    msgRet.heroSimple[0] = 0
    if helpInfo then
        msgRet.heroSimple[0] = 1
        HeroGrid.makeHeroSimple(msgRet.heroSimple[1], helpInfo.heroGrid, helpInfo.heroGrid.bagIndex)
    end
    msgRet.helpRed = hasHelpRed(human) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 派遣英雄，成功后返回GC_LIANYU_HELP_MY_QUERY
function selectMyHelp(human, heroIndex)
    local lyCommon = getLianyuCommon()
    local helpInfo = getHelpInfo(lyCommon, human.db._id)
    if helpInfo then
        return Broadcast.sendErr(human, Lang.LIANYU_MY_SELECT_ERR_HAD)
    end

    local heroGrid = human.db.heroBag[heroIndex]
    if type(heroGrid) ~= "table" then
        return Broadcast.sendErr(human, Lang.LIANYU_MY_SELECT_ERR_INDEX)
    end

    local combatObj = CombatLogic.createHumanObj(human, heroGrid.uuid)
    local rolebase = CombatLogic.createRoleBaseByDB(human.db)
    if not combatObj or not rolebase then
        return Broadcast.sendErr(human, Lang.LIANYU_MY_SELECT_ERR_INDEX)
    end
    addHelpInfo(lyCommon, human.db._id, heroGrid, combatObj, rolebase)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1205)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1201)
    sendHelpMyQuery(human)
    sendQuery(human)
end

-- 获取战斗中的累计奖励列表
function getFightItems(human, winLv)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    local oldWinLv = lianyuCache.oldWinLv
    if oldWinLv >= PER_FIVELEVEL then
        return -- 今日已获得过5关累计奖励，就不能继续获得了
    end
    winLv = winLv or (lianyuCache.winLv or 0)
    if winLv <= oldWinLv then
        return -- 今日没刷出更好的成绩
    end

    local items = {}
    local startLv = lianyuCache.startLv
    for i = oldWinLv + 1, winLv do
        local lv = startLv + i - 1
        local config = LianyuExcel.gamelevel[lv]
        if not config then
            break
        end

        local rewards = config.totalrewards
        if i > PER_FIVELEVEL then
            -- 每日前5关奖励特别好,后面的就不太好了
            rewards = config.fiverewards
        end
        for _, item in ipairs(rewards) do
            local itemID = item[1]
            local itemCnt = item[2]
            items[itemID] = (items[itemID] or 0) + itemCnt
        end
    end
    return items
end

-- 战斗界面额外信息
function sendFightInfo(human, isFight)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    local winLv = lianyuCache.winLv or 0
    local maxLv = getMaxLv(human)
    if isFight then
        if lianyuCache.combatResult == CombatDefine.RESULT_WIN then
            winLv = winLv - 1
            maxLv = maxLv - 1
        end
    end

    local lv = lianyuCache.startLv + winLv
    local itemID = getSelectBuffer(human)

    local msgRet = Msg.gc.GC_LIANYU_FIGHT_INFO
    local dataNet = msgRet.data
    dataNet.lv = lv
    Grid.makeItem(dataNet.buffer, itemID, 1)
    local items = getFightItems(human, winLv)
    dataNet.items[0] = 0
    dataNet.isAllGet = 0
    dataNet.needLv = 0
    dataNet.lastLv = 0
    if items then --有累计奖励
        for itemID, itemCnt in pairs(items) do
            dataNet.items[0] = dataNet.items[0] + 1
            local itemNet = dataNet.items[dataNet.items[0]]
            Grid.makeItem(itemNet, itemID, itemCnt)
        end
    else
        dataNet.items[0] = 2
        -- 发送给前端显示 累计奖励
        Grid.makeItem(dataNet.items[1], 111, 0)
        Grid.makeItem(dataNet.items[2], 101, 0)
        local oldWinLv = lianyuCache.oldWinLv
        if oldWinLv < PER_FIVELEVEL and winLv < PER_FIVELEVEL then --后面会有累计奖励
            dataNet.needLv = math.max(oldWinLv + 1 - winLv, 0)
            dataNet.lastLv = lianyuCache.startLv + PER_FIVELEVEL - 1
        else -- 后面不会有累计奖励了
            dataNet.isAllGet = 1
        end
    end
    local firstNeedLv, firstConfig = getNearFirstReward(human)
    dataNet.firstNeedLv = firstNeedLv or 0
    dataNet.firstItems[0] = firstConfig and #firstConfig.firstrewards or 0
    for i = 1, dataNet.firstItems[0] do
        local itemID = firstConfig.firstrewards[i][1]
        local itemCnt = firstConfig.firstrewards[i][2]
        Grid.makeItem(dataNet.firstItems[i], itemID, itemCnt)
    end
    dataNet.firstCanGet = (maxLv >= firstNeedLv) and 1 or 0
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 助战英雄出战
local HELP_MAIL_ITEMS = {{}}
function setHelpCombatPos(human, pos)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        lianyuCache = initCombatCache(human)
    end
    local helpInfo = setCombatHelpPos(human, pos)
    if not helpInfo then
        return
    end

    -- 助战给对方奖励
    if helpInfo.helpUuids[human.db._id] then
        return -- 同一个人助战不给
    end
    if helpInfo.helpCnt >= FRIEND_DEGREE_MAX then
        return -- 超过上限不给
    end
    setHelpTarget(helpInfo, human.db._id)

    HELP_MAIL_ITEMS[1][1] = ItemDefine.ITEM_FRIEND_ID
    HELP_MAIL_ITEMS[1][2] = FRIEND_ITEM_CNT

    local mailConfig = MailExcel.mail[MailDefine.MAIL_ID_LIANYU1]
    local title = mailConfig.title
    local senderName = mailConfig.senderName
    local content = Util.format(mailConfig.content, human.db.name, FRIEND_ITEM_CNT)
    MailManager.add(MailManager.SYSTEM, helpInfo.uuid, title, content, HELP_MAIL_ITEMS, senderName)
end

-- 弹出buffer界面
function sendBufferSelect(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    initCombatObjCache(human)
    local formationConfig = CombatPosLogic.getFormationConfig(lianyuCache.formation)
    local msgRet = Msg.gc.GC_LIANYU_BUFFER_SELECT_LIST
    msgRet.formation = lianyuCache.formation
    msgRet.formationName = formationConfig and formationConfig.name or ""
    msgRet.lv = lianyuCache.startLv + (lianyuCache.winLv or 0)
    msgRet.bufferList[0] = #lianyuCache.bufferList
    for i, itemID in ipairs(lianyuCache.bufferList) do
        Grid.makeItem(msgRet.bufferList[i], itemID, 1)
    end
    msgRet.heroList[0] = 0
    for index = 1, CombatDefine.COMBAT_HERO_CNT do
        local combatObj = lianyuCache.objList[index]
        if combatObj then
            msgRet.heroList[0] = msgRet.heroList[0] + 1
            local heroNet = msgRet.heroList[msgRet.heroList[0]]
            HeroGrid.makeHeroSimple(heroNet, combatObj, index)
            heroNet.hp = getCombatHpRate(human, index)
            heroNet.hpMax = MAX_HP_RATE
        end
    end
    --Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 选择增益buffer
function selectBuffer(human, bufferIndex)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    -- 不能重复选择
    if lianyuCache.bufferIndex then
        return
    end

    setBufferIndex(human, bufferIndex)
    fight(human)
end

-- 站前加属性
function onFightBegin(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    local itemID = getSelectBuffer(human)
    if not itemID then
        return
    end

    local cmd = ItemDefine.getValue(itemID, "cmd")
    for index = 1, CombatDefine.COMBAT_HERO_CNT do
        local atkPos = CombatLogic.getPos(CombatDefine.ATTACK_SIDE, index)
        local atkObj = CombatImpl.objList[atkPos]
        if atkObj then
            atkObj.isSysAttrChange = true
            if cmd[1] == "lianyuAttr" then
                local key = cmd[2][1]
                local value = cmd[2][2]
                atkObj.sysAttr[key] = atkObj.sysAttr[key] + value
            end
        end
    end
end

-- 回血+设置血量
function recalcHpFightBegin(human, index, atkObj)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    -- 援军
    local realIndex = lianyuCache.backupPos2index and lianyuCache.backupPos2index[index] or index
    local hpRate = getCombatHpRate(human, realIndex)
    local itemID = getSelectBuffer(human)
    local cmd = itemID and ItemDefine.getValue(itemID, "cmd")
    if cmd and cmd[1] == "lianyuHpMp" and (lianyuCache.winLv or 0) % PER_FIVELEVEL == 0 then
        local hpRateAdd = cmd[2][1] or 0
        hpRate = hpRate + hpRateAdd
    end

    local hpMax = CombatObj.getHpMax(atkObj)
    atkObj.hp = math.ceil(hpMax * hpRate / MAX_HP_RATE)
    atkObj.hp = math.min(atkObj.hp, hpMax)
end

-- 检查要不要结束
function checkFinishFight(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end
    if lianyuCache.combatResult and lianyuCache.combatResult ~= CombatDefine.RESULT_WIN then
        finishFight(human) -- 失败就结束吧
        return
    end
    local lv = lianyuCache.startLv + (lianyuCache.winLv or 0)
    local config = LianyuExcel.gamelevel[lv]
    if not config then -- 全部通关了也结束
        finishFight(human)
        return
    end
    return lv, config
end

-- 战斗开始
function fight(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        lianyuCache = initCombatCache(human)
    end
    local lv, config = checkFinishFight(human)
    if not lv or not config then
        return
    end

    if not lianyuCache.bufferIndex then
        -- 发送buffer选择界面
        return sendBufferSelect(human)
    end

    -- 标记今日打过
    if getTodayWinLv(human) == nil then
        setTodayWinLv(human, 0)
    end

    -- 战斗界面额外信息
    sendFightInfo(human)
    -- 调用战斗接口
    CombatLogic.combatBegin(human, config.mapID, nil, CombatDefine.COMBAT_TYPE8, lv)
    DailyTaskLogic.recordDailyTaskFinishCnt(human, DailyTaskLogic.DAILY_TASK_ID_8, 1)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1205)
    RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_1201)
    YunYingLogic.onCallBack(human, "onLianyu",1)   
end

-- 战斗信息
function setCombatBeginInfo(human, combatInfo, lv)
    -- 设置一些战斗信息
    combatInfo.defender.name = Util.format(Lang.COMBAT_LIANYU_NAME, lv)
end

-- 获取当前地图ID
function getMapID(human, args)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        lianyuCache = initCombatCache(human)
    end
    local lv = lianyuCache.startLv + (lianyuCache.winLv or 0)
    local config = LianyuExcel.gamelevel[lv]
    if not config then -- 全部通关了
        return
    end
    return config.mapID
end

-- 战斗结束回调
function onFightEnd(human, result, combatType, lv, combatInfo)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    lianyuCache.combatResult = result
    combatInfo.endStatus = CombatLogic.COMBAT_END_STATUS2
    combatInfo.noSendFinish = true
    if result == CombatDefine.RESULT_WIN then
        -- 胜利
        lianyuCache.winLv = (lianyuCache.winLv or 0) + 1
        if lv > getMaxLv(human) then
            setMaxLv(human, lv)
        end
        -- 每5关重置buffer
        if lianyuCache.winLv % PER_FIVELEVEL == 0 then
            randBuffers(human)
        end
        -- 记录血量/技能
        lianyuCache.combatHpRates = lianyuCache.combatHpRates or {}
        for index = 1, CombatDefine.COMBAT_HERO_CNT do
            local atkPos = CombatLogic.getPos(CombatDefine.ATTACK_SIDE, index)
            local atkObj = combatInfo.objList[atkPos]
            if atkObj then
                -- 援军
                local realIndex = lianyuCache.backupPos2index and lianyuCache.backupPos2index[index] or index
                local hpMax = CombatObj.getHpMax(atkObj)
                lianyuCache.combatHpRates[realIndex] = atkObj.hp * MAX_HP_RATE / hpMax
                if atkObj.backupPos then
                    local atkBackupPos = CombatLogic.getIndexByPos(CombatDefine.ATTACK_SIDE, atkObj.backupPos)
                    lianyuCache.backupPos2index = lianyuCache.backupPos2index or {}
                    lianyuCache.backupPos2index[atkBackupPos] = index
                end
            end
        end
        lianyuCache.combatObjSkill = lianyuCache.combatObjSkill or {}
        lianyuCache.combatHelpSkill = lianyuCache.combatHelpSkill or {}
        for atkPos, atkObj in pairs(combatInfo.objList) do
            if atkObj.side == CombatDefine.ATTACK_SIDE then
                local realIndex = atkObj.backupPos or atkPos
                local skill = {}
                skill.skillList = atkObj.skillList
                skill.beSkillList = atkObj.beSkillList
                lianyuCache.combatObjSkill[realIndex] = skill
            end
        end
        for atkPos, atkObj in pairs(combatInfo.helpList) do
            if atkObj.side == CombatDefine.ATTACK_SIDE then
                local skill = {}
                skill.skillList = atkObj.skillList
                skill.beSkillList = atkObj.beSkillList
                lianyuCache.combatHelpSkill[atkPos] = skill
            end
        end

        if lv >= #LianyuExcel.gamelevel then -- 全部通关
            finishFight(human, combatInfo)
        else
            if human.fd then
                CombatLogic.sendCombatFinish(human, combatInfo)
                fight(human)
            else
                if lianyuCache.bufferIndex == nil then
                    -- 自动选择BUFFER
                    selectBuffer(human, 1)
                else
                    fight(human)
                end
            end
        end

        MengxinLogic.onCallBack(human, MengxinLogic.MX_TASK_TYPE_7, lv)
        --TODO:记录绝望深渊
        Log.write(Log.LOGID_OSS_BATTLE_DESPAIRABYSS, human.db._id, human.db.account, human.db.name, lv)
    else
        -- 失败
        finishFight(human, combatInfo)
    end
end

-- 初始战斗对象信息
function initCombatObjCache(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache or lianyuCache.objList then
        return
    end
    local objList, helpList, rolebase, formation, jiban = CombatLogic.getHumanObjList(human, CombatDefine.COMBAT_TYPE8)
    local combatHelp, combatHelpPos = getCombatHelp(human)
    if combatHelp and combatHelpPos then
        if CombatPosLogic.checkPos(formation, combatHelpPos) then
            objList[combatHelpPos] = combatHelp.combatObj
        end
    end

    lianyuCache.objList = objList
    lianyuCache.helpList = helpList
    lianyuCache.rolebase = rolebase
    lianyuCache.formation = formation
    lianyuCache.jiban = jiban
end

-- 玩家战斗信息
function getCombatObjList(human, side)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    if side == CombatDefine.DEFEND_SIDE then
        local lv = lianyuCache.startLv + (lianyuCache.winLv or 0)
        local config = LianyuExcel.gamelevel[lv]
        if not config then
            return
        end
        return CombatLogic.getMonsterObjList(config.monsterOutID)
    end

    initCombatObjCache(human)
    local helpList = lianyuCache.helpList
    local rolebase = lianyuCache.rolebase
    local formation = lianyuCache.formation
    local objList = {}
    for index, obj in pairs(lianyuCache.objList) do
        if getCombatHpRate(human, index) > 0 then
            objList[index] = obj
        end
    end

    -- 援军
    if lianyuCache.backupPos2index then
        for backupPos, index in pairs(lianyuCache.backupPos2index) do
            local obj = lianyuCache.objList[index]
            if obj and getCombatHpRate(human, index) > 0 then
                objList[backupPos] = obj
                objList[index] = nil
            end
        end
    end
    return objList, helpList, rolebase, formation, lianyuCache.jiban
end

-- 请求结算（战斗结束/中途退出等）
function finishFight(human, combatInfo, noSend)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    local items = getFightItems(human)
    -- 刷新每日胜利次数
    if (lianyuCache.winLv or 0) > (getTodayWinLv(human) or 0) then
        setTodayWinLv(human, lianyuCache.winLv)
    end
    cleanCache(human)

    local combatItems = {}
    local msgRet = Msg.gc.GC_LIANYU_FIGHT_FINISH
    msgRet.items[0] = 0
    msgRet.data[0] = 0
    if items then
        for itemID, itemCnt in pairs(items) do
            msgRet.items[0] = msgRet.items[0] + 1
            local itemNet = msgRet.items[msgRet.items[0]]
            Grid.makeItem(itemNet, itemID, itemCnt)
            BagLogic.addItem(human, itemID, itemCnt, "lianyu_win_reward")
            combatItems[#combatItems + 1] = {itemID, itemCnt}
        end
    end

    if not combatInfo then
        local combatCache = CombatLogic.getCombatCache(human.db._id, CombatDefine.COMBAT_TYPE8)
        combatInfo = combatCache and combatCache.combatInfo
        CombatLogic.cleanCombatCache(human.db._id, CombatDefine.COMBAT_TYPE8)
    end
    if combatInfo then
        if #combatItems > 1 then
            combatInfo.endStatus = nil
            combatInfo.isWin = true
        else
            combatInfo.endStatus = CombatLogic.COMBAT_END_STATUS1
        end
        combatInfo.rewardItem = combatItems
        if not noSend then
            CombatLogic.sendCombatFinish(human, combatInfo)
        end
        CombatLogic.fontCombatFinish(msgRet.data[1], combatInfo)
        msgRet.data[0] = 1
    --		CombatLogic.sendCombatFinish(human, combatInfo)
    end
    --	Msg.trace(msgRet)
    Msg.send(msgRet, human.fd)
end

-- 助战英雄数
function getCombatZhuzhanCnt(human)
    local combatHelp, combatHelpPos = getCombatHelp(human)
    if combatHelp and combatHelpPos then
        return 1
    end
end

-- 是否显示跳过战斗按钮
function isCombatShowJumpBtn(human)
    return true
end

-- 继承技能CD
function setSkillAndBeskill(human, combatInfo)
    local lianyuCache = getLianyuCache(human)
	if not lianyuCache then return end

	for atkPos, atkObj in pairs(CombatImpl.objList) do
		local skill = lianyuCache.combatObjSkill and lianyuCache.combatObjSkill[atkPos]
		if atkObj.side == CombatDefine.ATTACK_SIDE and skill then
			atkObj.skillList = skill.skillList
			atkObj.beSkillList = skill.beSkillList
		end
		--print("===============", atkPos, skill and skill.skillList, skill and skill.beSkillList)
	end
	for atkPos, atkObj in pairs(CombatImpl.helpList) do
		local skill = lianyuCache.combatHelpSkill and lianyuCache.combatHelpSkill[atkPos]
		if atkObj.side == CombatDefine.ATTACK_SIDE and skill then
			atkObj.skillList = skill.skillList
			atkObj.beSkillList = skill.beSkillList
		end
		--print("===============", atkPos, skill and skill.skillList, skill and skill.beSkillList)
	end
end

-- ============================== 其它 ==================================

-- 每日重置
function updateDaily(human)
    if not human.db.lianyu then
        return
    end

    local lianyuDB = human.db.lianyu
    lianyuDB.todayMaxLv = getMaxLv(human)
    lianyuDB.todayWinLv = nil
    lianyuDB.todayHelpUuid = nil
end

-- 登出
function onLogout(human)
    checkFinishFight(human)
    -- 自动选择BUFFER
    selectBuffer(human, 1)
end

-- 登陆
function onLogin(human)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        return
    end

    local combatCache = CombatLogic.getCombatCache(human.db._id, CombatDefine.COMBAT_TYPE8)
    if combatCache then
        sendFightInfo(human)
    else
        finishFight(human)
    end
end

-- 0点结算发奖励
MAIL_SEND_CACHE = MAIL_SEND_CACHE or nil
function onZero()
    MAIL_SEND_CACHE = {}
    local maxCnt = LianyuExcel.rankreward[#LianyuExcel.rankreward].rankMax
    local board = BillboardDB.getBoard(BillboardDefine.TYPE_LIANYU)
    if board and board.rank2data then
        maxCnt = maxCnt <= #board.rank2data and maxCnt or #board.rank2data
        for i = 1, maxCnt do
            local rankData = board.rank2data[i]
            MAIL_SEND_CACHE[i] = rankData.uuid
        end
    end

    Timer.addLater(1, boardReward)
end

-- 定时器 邮件分开发，预防崩溃
function boardReward()
    if not MAIL_SEND_CACHE then
        return
    end

    for i = 1, #MAIL_SEND_CACHE do
        local uuid = MAIL_SEND_CACHE[i]
        local rankConfig = getRankConfig(i)
        if not rankConfig then
            MAIL_SEND_CACHE = nil
            return
        end

        local mailConfig = MailExcel.mail[MailDefine.MAIL_ID_LIANYU2]
        local title = mailConfig.title
        local senderName = mailConfig.senderName
        local content = Util.format(mailConfig.content, i)
        MailManager.add(MailManager.SYSTEM, uuid, title, content, rankConfig.rewards, senderName)
    end
    MAIL_SEND_CACHE = nil
end

-- 设置助战上阵
local HELP_MAIL_ITEMS = {{}}
function setCombatZhuzhan(human, pos)
    local lianyuCache = getLianyuCache(human)
    if not lianyuCache then
        lianyuCache = initCombatCache(human)
    end
    local helpInfo = setCombatHelpPos(human, pos)
    if not helpInfo then
        return
    end

    -- 助战给对方奖励
    if helpInfo.helpUuids[human.db._id] then
        return -- 同一个人助战不给
    end
    if helpInfo.helpCnt >= FRIEND_HELP_MAXCNT then
        return -- 超过上限不给
    end
    setHelpTarget(helpInfo, human.db._id)

    HELP_MAIL_ITEMS[1][1] = ItemDefine.ITEM_FRIEND_ID
    HELP_MAIL_ITEMS[1][2] = FRIEND_ITEM_CNT

    local mailConfig = MailExcel.mail[92]
    local title = mailConfig.title
    local senderName = mailConfig.senderName
    local content = Util.format(mailConfig.content, human.db.name, FRIEND_ITEM_CNT)
    MailManager.add(MailManager.SYSTEM, helpInfo.uuid, title, content, HELP_MAIL_ITEMS, senderName)
end

function isDot(human)
    local rewardRed = hasRewardRed(human)
    if rewardRed then
        return true
    end

    local lyCommon = getLianyuCommon()
    local helpInfo = getHelpInfo(lyCommon, human.db._id)
    if not helpInfo then
        return true
    end

    local todayWinLv = getTodayWinLv(human)
    if not todayWinLv then
        return true
    end

    return false
end
